#coding=utf-8
import logging
from sqlalchemy import Column, Integer, Unicode, asc
from ext.model import DBSession

log = logging.getLogger(__name__)

class TreeMixin():

    __tablename__ = None

    name = Column(Unicode(100))
    level = Column(Integer)
    lft = Column(Integer)
    rgt = Column(Integer)

    parent_id = Column(Integer)

    @property
    def parent(self):
        return DBSession.query(self.__class__).get(self.parent_id)

    @classmethod
    def find_tree(cls):
        return DBSession.query(cls).order_by(cls.lft).all()

    @classmethod
    def find_level(cls, level):
        return DBSession.query(cls).filter(cls.level == level).order_by(cls.lft).all()

    @classmethod
    def list_tree(cls):
        tree_list = [{'id': 0, 'name':'All %s' % cls.__classname__, 'open': 'true'}]
        for i in cls.find_tree():
            tree_list.append({'id': i.id, 'pId':i.parent_id, 'name':i.name, 'open': 'true'})
        return tree_list

    @classmethod
    def get_tree_leaf(cls, id):
        return DBSession.query(cls).get(id)

    def create_tree_leaf(self):
        try:
            if not self.parent_id or int(self.parent_id) == 0:
                max_rgt = DBSession.query(func.max(self.__class__.rgt)).one()[0]
                max_rgt = max_rgt if max_rgt else 0
                self.lft = max_rgt + 1
                self.rgt = max_rgt + 2
                self.level = 1
            else:
                parent = self.__class__.get(self.parent_id)
                self.level = parent.level + 1
                DBSession.execute('update %s set lft=lft+2 where rgt>%s' % (self.__tablename__, parent.rgt))
                DBSession.execute('update %s set rgt=rgt+2 where rgt>=%s' % (self.__tablename__, parent.rgt))
                self.lft = parent.rgt
                self.rgt = parent.rgt + 1
            self.name = 'unknown'
            DBSession.add(self)
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def edit_tree_leaf(self, name=None):
        try:
            if not name and not self.name:
                self.name = 'unknown %s' % self.id
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def remove_tree_leaf(self):
        try:
            if int(self.id) == 0:
                DBSession.execute('delete from %s' % self.__tablename__)
            else:
                width = self.rgt - self.lft + 1
                DBSession.execute('delete from %s where lft between %s and %s' % (self.__tablename__, self.lft, self.rgt))
                DBSession.execute('update %s set lft=lft-%s where lft>%s' % (self.__tablename__, width, self.rgt))
                DBSession.execute('update %s set rgt=rgt-%s where rgt>%s' % (self.__tablename__, width, self.rgt))
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def move_tree_leaf(self, target_id, move_type):
        try:
            cls = self.__class__
            width = self.rgt - self.lft + 1
            DBSession.execute('update %s set active=0, lft=lft-%s, rgt=rgt-%s, level=level-%s where lft between %s and %s' % (self.__tablename__, self.lft - 1, self.lft - 1, self.level - 1, self.lft, self.rgt))
            DBSession.execute('update %s set lft=lft-%s where lft>%s and active==1' % (self.__tablename__, width, self.rgt))
            DBSession.execute('update %s set rgt=rgt-%s where rgt>%s and active==1' % (self.__tablename__, width, self.rgt))
            target = cls.get_tree_leaf(target_id)
            if move_type == 'inner':
                DBSession.execute('update %s set lft=lft+%s where lft>%s and active==1' % (self.__tablename__, width, target.rgt))
                DBSession.execute('update %s set rgt=rgt+%s where rgt>=%s and active==1' % (self.__tablename__, width, target.rgt))
                target = cls.get_tree_leaf(target_id)
                _self = cls.get_tree_leaf(self.id)
                DBSession.execute('update %s set parent_id=%s where id=%s' % (self.__tablename__, target.id, self.id))
                minus = target.rgt - _self.rgt - 1
                DBSession.execute('update %s set lft=lft+%s, rgt=rgt+%s, level=level+(%s), active=1 where active==0' % (self.__tablename__, minus, minus, target.level - _self.level + 1))
            elif move_type == 'before':
                DBSession.execute('update %s set lft=lft+%s where lft>=%s and active==1' % (self.__tablename__, width, target.lft))
                DBSession.execute('update %s set rgt=rgt+%s where rgt>%s and active==1' % (self.__tablename__, width, target.lft))
                target = cls.get_tree_leaf(target_id)
                _self = cls.get_tree_leaf(self.id)
                minus = target.lft - _self.lft - width
                DBSession.execute('update %s set parent_id=%s where id=%s' % (self.__tablename__, target.parent_id, self.id))
                DBSession.execute('update %s set lft=lft+%s, rgt=rgt+%s, level=level+(%s), active=1 where active==0' % (self.__tablename__, minus, minus, target.level - _self.level))
            elif move_type == 'after':
                DBSession.execute('update %s set lft=lft+%s where lft>%s and active==1' % (self.__tablename__, width, target.rgt))
                DBSession.execute('update %s set rgt=rgt+%s where rgt>%s and active==1' % (self.__tablename__, width, target.rgt))
                target = cls.get_tree_leaf(target_id)
                _self = cls.get_tree_leaf(self.id)
                minus = target.rgt
                DBSession.execute('update %s set parent_id=%s where id=%s' % (self.__tablename__, target.parent_id, self.id))
                DBSession.execute('update %s set lft=lft+%s, rgt=rgt+%s, level=level+(%s), active=1 where active==0' % (self.__tablename__, minus, minus, target.level - _self.level))
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

class BranchMixin():

    __tablename__ = None
    __branch__ = {'active':['Type', 'active', 'active_order']}

    name = Column(Unicode(100))
    active_order = Column(Integer)

    @classmethod
    def get_attr(cls, branch=None):
        if not branch:
            branch = cls.__branch__.keys()[0]
        arr = cls.__branch__.get(branch)
        attr = cls()
        attr._name = arr[0]
        attr._type = arr[1]
        attr._order = arr[2]
        attr._type_attr = getattr(cls, attr._type)
        attr._order_attr = getattr(cls, attr._order)
        return attr

    @classmethod
    def list_branch(cls, branch=None):
        attr = cls.get_attr(branch)
        branch_list = [{'id': 0, 'name':'%s' % attr._name, 'open': 'true'}]
        for i in DBSession.query(cls).filter(attr._type_attr == 1).order_by(asc(attr._order_attr)).all():
            branch_list.append({'id': i.id, 'pId':0, 'name':i.name, 'open': 'true'})
        return branch_list

    @classmethod
    def find_branch(cls, branch=None):
        attr = cls.get_attr(branch)
        branch_list = DBSession.query(cls).filter(attr._type_attr == 1).order_by(asc(attr._order_attr)).all()
        branch_list.insert(0, cls(** dict(name=u'Other', id=0)))
        return branch_list

    def set_to_branch(self, branch=None):
        try:
            attr = self.__class__.get_attr(branch)
            if getattr(self, attr._type) != 1:
                max_order = DBSession.query(func.max(attr._order_attr)).one()[0]
                setattr(self, attr._type, 1)
                setattr(self, attr._order, max_order + 1 if max_order else 1)
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def cancel_from_branch(self, branch=None):
        try:
            attr = self.__class__.get_attr(branch)
            if getattr(self, attr._type) != 0:
                self_order = getattr(self, attr._order)
                DBSession.execute('update %s set %s=%s-1 where %s > %s' % (self.__tablename__, attr._order, attr._order, attr._order, self_order))
                setattr(self, attr._type, 0)
                setattr(self, attr._order, None)
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def create_branch(self):
        try:
            self.name = 'unknown'
            DBSession.add(self)
            self.set_to_branch()
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise
        
    def edit_branch(self, name=None):
        try:
            if not name and not self.name:
                self.name = 'unknown %s' % self.id
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def remove_branch(self):
        try:
            self.cancel_from_branch()
            DBSession.delete(self)
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

    def move_branch(self, target_id, move_type, branch=None):
        try:
            attr = self.__class__.get_attr(branch)
            target = self.__class__.get(target_id)
            self_order = getattr(self, attr._order)
            target_order = getattr(target, attr._order)
            if move_type == 'before':
                if self_order > target_order:
                    DBSession.execute('update %s set %s=%s+1 where %s >= %s and %s < %s' % (self.__tablename__, attr._order, attr._order, attr._order, target_order, attr._order, self_order))
                    self_order = target_order
                else:
                    DBSession.execute('update %s set %s=%s-1 where %s < %s and %s > %s' % (self.__tablename__, attr._order, attr._order, attr._order, target_order, attr._order, self_order))
                    self_order = target_order - 1
            elif move_type == 'after':
                if self_order > target_order:
                    DBSession.execute('update %s set %s=%s+1 where %s > %s and %s < %s' % (self.__tablename__, attr._order, attr._order, attr._order, target_order, attr._order, self_order))
                    self_order = target_order + 1
                else:
                    DBSession.execute('update %s set %s=%s-1 where %s <= %s and %s > %s' % (self.__tablename__, attr._order, attr._order, attr._order, target_order, attr._order, self_order))
                    self_order = target_order
            setattr(self, attr._order, self_order)
        except Exception, e:
            transaction.doom()
            log.exception(str(e))
            raise

